#include <iostream>
#include <vector>
#include <cassert>
#include <set>
#include <tuple>
#include <climits>
using namespace std;
const int dirs[5][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}, {0, 0}};
int get_value(const vector<int>& v){
int res = 0;
for(int e: v) res = res * 2 + e;
return res;
}
void dfs_next(int n, int index, vector<int>& state, set<tuple<int, int, int>>& res){
if(index == n){
vector<vector<int>> g(3, vector<int>(n, 0));
for(int c = 0; c < n; c ++){
int nr = 1 + dirs[state[c]][0], nc = c + dirs[state[c]][1];
assert(0 <= nr && nr < 3 && 0 <= nc && nc < n);
g[nr][nc] = 1;
}
tuple<int, int, int> tres;
get<0>(tres) = get_value(g[0]);
get<1>(tres) = get_value(g[1]);
get<2>(tres) = get_value(g[2]);
res.insert(tres);
return;
}
for(int d = 0; d < 5; d ++){
if(index == n - 1 && d == 0) continue;
if(index == 0 && d == 2) continue;
state[index] = d;
dfs_next(n, index + 1, state, res);
}
}
void get_next(int n, set<tuple<int, int, int>>& res){
vector<int> state(n);
dfs_next(n, 0, state, res);
}
int dfs(int n, int index, int state0, int state1,
vector<vector<vector<int>>>& dp, const set<tuple<int, int, int>>& next){
if(dp[index][state0][state1] != INT_MAX) return dp[index][state0][state1];
int res = INT_MAX;
if(index == n - 1){
for(const tuple<int, int, int>& t: next){
if(get<2>(t)) continue;
int a = get<0>(t), b = get<1>(t);
res = min(res, __builtin_popcount(a | state0) + __builtin_popcount(b | state1));
}
}
else{
for(const tuple<int, int, int>& t: next){
int a = get<0>(t), b = get<1>(t), c = get<2>(t);
res = min(res, __builtin_popcount(a | state0) + dfs(n, index + 1, state1 | b, c, dp, next));
}
}
return dp[index][state0][state1] = res;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int R, C; cin >> R >> C;
if(R < C) swap(R, C); // R > C
set<tuple<int, int, int>> next;
get_next(C, next);
if(R == 1){
int res = INT_MAX;
for(const tuple<int, int, int>& t: next){
if(get<0>(t) || get<2>(t)) continue;
res = min(res, __builtin_popcount(get<1>(t)));
}
cout << R * C - res << '\n';
return 0;
}
vector<vector<vector<int>>> dp(R, vector<vector<int>>(1 << C, vector<int>(1 << C, INT_MAX)));
int res = INT_MAX;
for(const tuple<int, int, int>& t: next){
if(get<0>(t)) continue;
int state0 = get<1>(t), state1 = get<2>(t);
res = min(res, dfs(R, 1, state0, state1, dp, next));
}
cout << R * C - res << '\n';
return 0;
}
80A - Panoramix's Prediction | 1354B - Ternary String |
122B - Lucky Substring | 266B - Queue at the School |
1490A - Dense Array | 1650B - DIV + MOD |
1549B - Gregor and the Pawn Game | 553A - Kyoya and Colored Balls |
1364A - XXXXX | 1499B - Binary Removals |
1569C - Jury Meeting | 108A - Palindromic Times |
46A - Ball Game | 114A - Cifera |
776A - A Serial Killer | 25B - Phone numbers |
1633C - Kill the Monster | 1611A - Make Even |
1030B - Vasya and Cornfield | 1631A - Min Max Swap |
1296B - Food Buying | 133A - HQ9+ |
1650D - Twist the Permutation | 1209A - Paint the Numbers |
1234A - Equalize Prices Again | 1613A - Long Comparison |
1624B - Make AP | 660B - Seating On Bus |
405A - Gravity Flip | 499B - Lecture |